﻿#region Using Statements

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

#endregion


namespace Freeform
{

    #region SpatialProperties

    /// <summary>
    /// Spatial properties define the position and orientation of a body in 2D space.
    /// </summary>
    public class SpatialProperties
    {

        #region Events

        /// <summary>
        /// This delegate represents the handler form for all Property Changed events.
        /// </summary>
        /// <param name="item">A reference to the spatial properties which triggered the event.</param>
        public delegate void PropertyChangedEventHandler(SpatialProperties properties);


        /// <summary>
        /// This event is fired when the position property is changed.
        /// </summary>
        public event PropertyChangedEventHandler PositionChanged;


        /// <summary>
        /// This event is fired when the size property is changed.
        /// </summary>
        public event PropertyChangedEventHandler SizeChanged;


        /// <summary>
        /// This event is fired when the direction property is changed.
        /// </summary>
        public event PropertyChangedEventHandler DirectionChanged;


        /// <summary>
        /// This event is fired when the motion handler is changed.
        /// </summary>
        public event PropertyChangedEventHandler MotionHandlerChanged;

        #endregion


        #region Members

        /// <summary>
        /// The position of the body in 2D space.
        /// </summary>
        public virtual Vector2 Position
        {
            get { return _Position; }
            set
            {
                if (_Position == value) return;
                _Position = value;
                if (PositionChanged != null)
                    PositionChanged(this);
            }
        }
        internal Vector2 _Position = Vector2.Zero;


        /// <summary>
        /// The orientation of the body in radians.
        /// </summary>
        public virtual Single Direction
        {
            get { return _Direction; }
            set
            {
                if (_Direction == value) return;
                _Direction = value;
                if (DirectionChanged != null)
                    DirectionChanged(this);
            }
        }
        internal Single _Direction = 0;


        /// <summary>
        /// Gets or sets the size of the body in world units.
        /// </summary>
        public virtual Vector2 Size
        {
            get { return _Size; }
            set
            {
                if (_Size == value) return;
                _Size = value;
                if (SizeChanged != null)
                    SizeChanged(this);
            }
        }
        internal Vector2 _Size = Vector2.One;


        /// <summary>
        /// Gets or sets the motion controller for this body.
        /// </summary>
        public virtual MotionController MotionController
        {
            get { return _MotionController; }
            set
            {
                if (_MotionController == value) return;
                _MotionController = value;
                if (MotionHandlerChanged != null)
                    MotionHandlerChanged(this);
            }
        }
        internal MotionController _MotionController = null;

        #endregion


        #region Init

        /// <summary>
        /// Creates a new set of spatial properties with default values.
        /// </summary>
        public SpatialProperties()
        {
            Position = Vector2.Zero;
            Direction = 0;
            Size = Vector2.One;
        }

        #endregion

    }

    #endregion


    #region MotionController

    /// <summary>
    /// A motion controller is used to control how an entity adjusts its spatial properties with time.
    /// </summary>
    public class MotionController
    {

        #region Members

        /// <summary>
        /// Gets the spatial properties that this motion controller adjusts.
        /// </summary>
        public virtual SpatialProperties SpatialProperties { get; private set; }

        #endregion


        #region Init

        /// <summary>
        /// Creates a new motion controller.
        /// </summary>
        /// <param name="spatialProperties">The spatial properties that this motion controller will control.</param>
        public MotionController(SpatialProperties spatialProperties)
        {
            SpatialProperties = spatialProperties;
            SpatialProperties.MotionController = this;
        }

        #endregion


        #region Update

        /// <summary>
        /// Allows the motion controller to update the entity's position and direction.
        /// </summary>
        /// <param name="entity">The entity to update.</param>
        /// <param name="time">Provides a snapshot of timing values.</param>
        public virtual void Update(GameTime time) { }

        #endregion

    }

    #endregion


    #region ProcessProperties

    /// <summary>
    /// Process properties allow you to control how a function is computed.
    /// </summary>
    public class ProcessProperties
    {

        #region Members

        /// <summary>
        /// Gets a reference to the thread that is used to handle this function if multi-threading is used.
        /// </summary>
        public virtual Thread Thread { get; internal set; }


        /// <summary>
        /// Gets the handling type for this function.
        /// </summary>
        public virtual ProcessHandling ProcessHandling
        {
            get { return _ProcessHandling; }
            set
            {
                _ProcessHandling = value;

#if XBOX
#else
                if (_ProcessHandling != ProcessHandling.Multithread)
                    Thread = null;
#endif
            }
        }
        private ProcessHandling _ProcessHandling = ProcessHandling.None;

        #endregion


        #region Init

        /// <summary>
        /// Creates a new set of process properties.
        /// </summary>
        public ProcessProperties() { }

        #endregion
    }

    #endregion


    #region ProcessHandling

    /// <summary>
    /// Determines the method of computing a function.
    /// </summary>
    public enum ProcessHandling
    {
        /// <summary>
        /// If a function is flagged as sequential it will be undertaken in the main process thread.
        /// </summary>
        Sequential,

#if XBOX
#else
        /// <summary>
        /// If a function is flagged as multithread it will be undertaken in a separate thread.
        /// </summary>
        Multithread,
#endif

        /// <summary>
        /// If a function is flagged as none it will not be executed.
        /// </summary>
        None

    }

    #endregion


    #region Random

    /// <summary>
    /// The random class wraps the System.Random number generator and provides extra functionality.
    /// </summary>
    public class Random
    {

        #region Members

        /// <summary>
        /// Gets the System.Random used under the hood by this class.
        /// </summary>
        public System.Random Base { get; private set; }

        #endregion


        #region Init

        /// <summary>
        /// Creates a new random number generator.
        /// </summary>
        public Random()
        {
            Base = new System.Random();
        }

        #endregion


        #region Utility

        /// <summary>
        /// Generates a random floating-point precision value between the given bounds.
        /// </summary>
        /// <param name="lowerBound">The lower bound on the value.</param>
        /// <param name="upperBound">The upper bound on the value.</param>
        /// <returns>A random number.</returns>
        public Single Single(Single lowerBound, Single upperBound)
        {
            return (Single)(Base.NextDouble() * (upperBound - lowerBound) + lowerBound);
        }


        /// <summary>
        /// Generates a random double floating-point precision value between the given bounds.
        /// </summary>
        /// <param name="lowerBound">The lower bound on the value.</param>
        /// <param name="upperBound">The upper bound on the value.</param>
        /// <returns>A random number.</returns>
        public Double Double(Double lowerBound, Double upperBound)
        {
            return Base.NextDouble() * (upperBound - lowerBound) + lowerBound;
        }


        /// <summary>
        /// Generates a random integer between the given bounds.
        /// </summary>
        /// <param name="lowerBound">The lower bound on the value. (Inclusive)</param>
        /// <param name="upperBound">The upper bound on the value. (Exclusive)</param>
        /// <returns>A random number.</returns>
        public Int32 Int32(Int32 lowerBound, Int32 upperBound)
        {
            return Base.Next(lowerBound, upperBound);
        }

        #endregion

    }

    #endregion


    #region Util

    public abstract class Util
    {
        /// <summary>
        /// Returns a unit Vector2 in the direction specified.
        /// </summary>
        /// <param name="Direction">The direction for the unit vector, in radians.</param>
        /// <returns>A unit vector in the given direction.</returns>
        public static Vector2 Vec2InDir(Single Direction)
        {
            return new Vector2((Single)Math.Sin(Direction), -(Single)Math.Cos(Direction));
        }
    }

    #endregion

}
